package gbench.sandbox.matrix.img;

import org.junit.jupiter.api.Test;

import java.awt.Color;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import gbench.common.fs.XlsFile.DataMatrix;
import gbench.sandbox.matrix.img.util.BlasMatrix;
import gbench.sandbox.matrix.img.util.MatlibCanvas;
import gbench.sandbox.matrix.img.util.MatlibCanvas.NVec;

import static java.lang.Math.*;
import static gbench.sandbox.matrix.img.util.ImageOps.*;
import static gbench.sandbox.matrix.img.util.MatlibCanvas.*;
import static gbench.common.tree.LittleTree.*;

/**
 * 绘图 :点集合的变换
 * @author gbench
 *
 */
public class JunitMatlibTransform {
    
    /**
     * 绘图 <br>
     * 注意：此处的 线 除非特别说明，一般指 曲线，不一定 是直线，但也可以是 直线，即 一般意义上的 线。
     * @param size 窗口大小
     * @param plotflag 绘图标记，是否打开 图形窗口，true:打开,false:不打开
     */
    public void plot(final Integer size,final Boolean plotflag) {
        
        final var matlib = new MatlibCanvas(size); // 数学绘图（函数式的描述曲线）画布 组件
        
        matlib.plot(size, size, pen -> {
            
            final var theta = PI/5; // 旋转的角度
            final var r = 100; // 半径
            final var centerx = 250; // 圆心x
            final var centery = r; // 圆心y
            
            //直线旋转
            pen.drawLine(x->100d);
            pen.setColor(clr2rgb(Color.BLUE));
            pen.drawLine(x->100d,points->rotateMx(PI/5).mmult(points));
            
            // 旋转前的椭圆
            pen.setColor(clr2rgb(Color.CYAN)); // 修改默认颜色为 蓝绿色即青色
            pen.drawStraightLine(0, 0, centerx, centery);
            pen.drawPoints(range2pi(5000).map(matlib::rhoTheta)
                .map(e->e.affine(r,r/3,centerx,centery)));// 水平
            pen.setColor(clr2rgb(Color.YELLOW)); // 修改默认颜色为 蓝绿色即青色
            
            // 旋转后的椭圆
            pen.drawPoints( range2pi(5000).map(matlib::rhoTheta).map(e->e.affine(r,r/3,centerx,centery)),
                // 旋转 theta角度
                rotate(theta));// 旋转
            
            final var center_new = rotate(theta).apply(xyZero(250,100)).corece(e->e.intValue());// 旋转后的圆心,新的圆心
            pen.drawStraightLine(0, 0, center_new.get(0),  center_new.get(1));
            
            // 旋转+平移
            pen.setColor(clr2rgb(Color.RED)); // 修改默认颜色为 蓝绿色即青色
            final var points_or_points2_flag = false; // 旋转平移的 方案
            //椭圆的旋转与平移
            if(points_or_points2_flag)
                pen.drawPoints( range2pi(5000).map(matlib::rhoTheta).map(e->e.affine(r,r/3,centerx,centery)),
                    // 对于要做 整个椭圆的旋转  操作,移动到原点进行旋转，这一点很关键。旋转之后再进行 逆向平移，这里的次序不能乱，要用心体会。
                    px -> rotateMx(PI/2).mmult(px.add(xyZero(-centerx,-centery))) // 先移动到原点，然后旋转。
                        .add(xyZero(centerx,centery) // 然后在移动到新的坐标位置
                ));// drawPoints
            else // 使用 drawPoints2 绘制 
                pen.drawPoints2( range2pi(5000).map(matlib::rhoTheta).map(e->e.affine(r,r/3,centerx,centery)),NVec::toArray2,
                     // 对于要做 整个椭圆的旋转  操作,移动到原点进行旋转，这一点很关键。旋转之后再进行 逆向平移，这里的次序不能乱，要用心体会。
                      px -> {
                            final var ar = new AtomicReference<List<int[]>>(); // 闭包外空间,用作一个 可以修改值的 Optional 对象。
                            final var blas_flag = true; // 是否启用 blas,blas 启动需要时间,单次使用消耗时间长,单多次重复使用消耗低
                            final var times = 100; // 重复使用的时间消耗
                            final var total = Stream.iterate(0d,i->i<times,i->i+1d).map( i -> // 统计每次变换的时间消耗
                                timeit( () -> {
                                    if(blas_flag) {
                                        final var mx = BlasMatrix.of(px).transpose();
                                        final var y = xyZero(-centerx,-centery,BlasMatrix::of);
                                        final var _y = y.mul(-1d);
                                        final var mm = rotateMx(PI/2,BlasMatrix::of).mmul(mx.addColumnVector(y)).addColumnVector(_y);
                                        final var ll = BlasMatrix.list(mm.transpose(),compose_f(NVec::ints,NVec::new));
                                        ar.set(ll); // 设置返回结果
                                    }else { // 与blas 约有100多倍的 差距,DataMatrix 在做大规模 运算的使用 垃圾的一塌糊涂。
                                        final var mx = DataMatrix.of(px,true).transpose();
                                        final var mm = rotateMx(PI/2).mmult(mx.add(xyZero(-centerx,-centery))).add(xyZero(centerx,centery));
                                        final var ll = mm.colS().map(NVec::new).map(NVec::ints).collect(Collectors.toList());
                                        ar.set(ll); // 设置返回结果
                                    }// if blas_flag
                                } ) // timeit
                            ).collect(Collectors.summingDouble(e->e));
                            
                            // 显示累积历时时间消耗
                            System.out.println("total last for:"+total+" ms,\tavg:"+total/times+"ms");
                            
                            // 返回结果
                            return ar.get();
                      }// px
                  );// drawPoints2
            
        },plotflag); // plot
        
    }
    
    @Test
    public void bar() {
        // 绘制图形
        plot(size,plotflag);
        
        // 生成一个2PI[-PI,PI]周期中的  6个采样数据点
        range2pi(6-1).forEach((i,d)->System.out.println(i+"#\t"+d));
    }
    
    /**
     * 维度空间的生成
     */
    @Test
    public void qux() {
        final var rng = Range.of(2);// 0,1
        
        // 以rng为轴 依次 逐个 调用 衍生化 维度 张成器 以渐进的方式 做维度递增操作，进而构造多维空间，此处是4维
        rng.flatMap(rng.derived_2d_ctor()) // 1维->2维 扩增空间
           .flatMap(rng.derived_nd_ctor()) // 2维->3维 扩增空间
           .flatMap(rng.derived_nd_ctor()) // 3维->4维 扩增空间
           .map(kvp()).forEach(e->{ // 映射 增加 索引序号，并给予遍历
                System.out.println(e._1()+"#"+LIST(e._2()));
           });
        
        System.out.println("\nspan 无需逐次递增维度，一次性就可以张成多维空间");
        // 以rng为轴 一次性 调用 多个 衍生化 维度 张成器 ，张成 多维空间，此处是4维
        rng.span(rng.derived_2d_ctor(),rng.derived_nd_ctor(),Range.of(3).derived_nd_ctor())
           .map(kvp()).forEach(e->{ // 映射 增加 索引序号，并给予遍历
                System.out.println(e._1()+"-->"+LIST(e._2()));
           });
    }

    public Integer size=500; // 图片尺寸单位
    public Boolean plotflag=false; // 绘图标记
    
    /**
     * 
     * @param args
     */
    public static void main(String args[]) {
        final var jmlc = new JunitMatlibTransform();
        jmlc.plot(jmlc.size, true);
    }
    
}
